package com.tencent.sr.iris.basic.saas.service.activity;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.tencent.sr.iris.activity.interfaces.enums.IsParticipatedEnum;
import com.tencent.sr.iris.activity.interfaces.request.IrisPaginationRequest;
import com.tencent.sr.iris.activity.interfaces.request.acticity.ActivityTaskRequest;
import com.tencent.sr.iris.activity.interfaces.request.acticity.BatchDataRequest;
import com.tencent.sr.iris.activity.interfaces.response.SearchAfterPaginationResponse;
import com.tencent.sr.iris.activity.interfaces.response.activity.ActivityTierResponse;
import com.tencent.sr.iris.activity.interfaces.response.activity.BatchDataResponse;
import com.tencent.sr.iris.activity.interfaces.response.activity.BatchStatisticsResponse;
import com.tencent.sr.iris.basic.saas.common.enums.*;
import com.tencent.sr.iris.basic.saas.common.exception.BizErrorCode;
import com.tencent.sr.iris.basic.saas.common.utils.AssertUtils;
import com.tencent.sr.iris.basic.saas.common.utils.SpelUtils;
import com.tencent.sr.iris.basic.saas.dependency.activity.ActivityTaskDependency;
import com.tencent.sr.iris.basic.saas.dependency.user.UserDependency;
import com.tencent.sr.iris.basic.saas.interfaces.request.activity.ActivityTaskPageRequest;
import com.tencent.sr.iris.basic.saas.interfaces.response.activity.ActivityTaskPageResponse;
import com.tencent.sr.iris.basic.saas.interfaces.response.activity.SentAwardInfo;
import com.tencent.sr.iris.basic.saas.service.constant.ActivityRedisKeyConstant;
import com.tencent.sr.iris.basic.saas.service.convert.ActivityTaskConvert;
import com.tencent.sr.iris.user.core.service.interfaces.dto.UserChannelAccountDTO;
import com.tencent.sr.iris.user.core.service.interfaces.dto.user.UserWideDTO;
import com.tencent.sr.iris.user.core.service.interfaces.enums.RoleTypeEnum;
import com.tencent.sr.iris.user.core.service.interfaces.request.relation.BatchSupervisorRequest;
import com.tencent.sr.iris.user.core.service.interfaces.request.user.IrisUserWideListQueryRequest;
import com.tencent.sr.iris.user.core.service.interfaces.response.relation.SupervisorUserResponse;
import com.tencent.sr.iris.user.core.service.interfaces.response.staff.StaffDataPermDqGroupIdResponse;
import com.tencent.sr.rmall.common.exception.TMallBizException;
import com.tencent.sr.rmall.springbootstarter.redis.RedisClient;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.*;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.Collectors;

import static com.tencent.sr.iris.basic.saas.service.constant.ActivityRedisKeyConstant.BATCH_EXPIRE_SET_SCRIPT;

/**
 * 活动任务
 *
 * @author nixiaolin
 */
@Service
@Slf4j
public class ActivityTaskService {

    @Resource
    private ActivityTaskDependency activityTaskDependency;

    @Resource
    private ActivityTaskConvert activityTaskConvert;

    @Resource
    private UserDependency userDependency;

    @Resource
    private RedisClient redisClient;


    public SearchAfterPaginationResponse<ActivityTaskPageResponse> page(IrisPaginationRequest<ActivityTaskPageRequest> request) {

        IrisPaginationRequest<BatchDataRequest> paginationRequest = activityTaskConvert.buildBatchDataRequest(request);
        handleAuths(request.getQueryCriteria().getCurrentUserUid(), paginationRequest.getQueryCriteria());
        SearchAfterPaginationResponse<BatchDataResponse> page = activityTaskDependency.page(paginationRequest);

        SearchAfterPaginationResponse<ActivityTaskPageResponse> response = new SearchAfterPaginationResponse<>();
        response.setPageSize(page.getPageSize());
        response.setTotalCount(page.getTotalCount());
        response.setPageNum(page.getPageNum());
        response.setDataList(Collections.emptyList());

        List<BatchDataResponse> dataList = page.getDataList();
        if (CollectionUtil.isEmpty(dataList)) {
            return response;
        }
        //处理下级用户信息
        List<String> uidList = dataList.stream().map(BatchDataResponse::getUid)
                .filter(StrUtil::isNotBlank).distinct().collect(Collectors.toList());

        List<String> tUidList = dataList.stream().map(BatchDataResponse::getAdvisorUid)
                .filter(StrUtil::isNotBlank).distinct().collect(Collectors.toList());

        List<String> pidList = dataList.stream().map(BatchDataResponse::getPid)
                .filter(StrUtil::isNotBlank).distinct().collect(Collectors.toList());
        List<String> allUidList = CollectionUtil.addAllIfNotContains(pidList, CollectionUtil.addAllIfNotContains(uidList, tUidList));

        ActivityTaskPageRequest queryCriteria = request.getQueryCriteria();
        Long saasId = queryCriteria.getCurrentUserSaaSId();
        Map<String, UserWideDTO> userInfoMap = queryUserInfo(saasId, allUidList);

        String searchAfter = uidList.stream().max(String::compareTo).orElse(null);
        response.setSearchAfter(searchAfter);
        response.setPreSearchAfter(request.getQueryCriteria().getSearchAfter());

        List<ActivityTaskPageResponse> results;
        String groupByExpression = dataList.get(0).getGroupByExpression();
        if (StrUtil.isNotBlank(groupByExpression)) {
            results = handleMultiTask(dataList, userInfoMap);
        } else {
            results = handleSingleTask(dataList, userInfoMap);
        }

        response.setDataList(results);
        return response;
    }

    /**
     * 活动统计
     *
     * @param request
     * @return
     */
    public BatchStatisticsResponse activityStatic(ActivityTaskPageRequest request) {
        BatchDataRequest batchDataRequest = activityTaskConvert.buildBatchDataRequest(request);
        handleAuths(request.getCurrentUserUid(), batchDataRequest);
        return activityTaskDependency.activityStatic(batchDataRequest);
    }

    /**
     * 获取活动的最大阶数
     *
     * @param request
     * @return
     */
    public ActivityTierResponse getActivityMaxTier(ActivityTaskRequest request) {
        return activityTaskDependency.getActivityMaxTier(request);
    }

    /**
     * 处理
     *
     * @param uid
     * @param request
     */
    private void handleAuths(String uid, BatchDataRequest request) {
        StaffDataPermDqGroupIdResponse response = userDependency.queryStaffDqGroupIdListByUid(uid);
        if (!Objects.equals(response.getRoleType(), RoleTypeEnum.PLATFORM_ROLE.getCode().toString())) {
            List<String> dqGroupIdList = response.getDqGroupIdList();
            if (CollectionUtil.isEmpty(dqGroupIdList)) {
                throw new TMallBizException("当前用户无数据权限");
            }
            request.setAreaAuths(dqGroupIdList.stream().map(Long::valueOf).collect(Collectors.toList()));
        }
    }

    /**
     * 查询导师信息
     *
     * @param uidList
     * @return
     */
    private Map<String, SupervisorUserResponse> querySupervisor(List<String> uidList) {
        Function<String, String> keyFunc = i -> String.format(ActivityRedisKeyConstant.USER_SUPERVISOR_KEY, i);
        Function<String, SupervisorUserResponse> deSerializerFunc = i -> JSON.parseObject(i, SupervisorUserResponse.class);
        Function<SupervisorUserResponse, String> mapFunc = SupervisorUserResponse::getUid;
        Function<BatchSupervisorRequest, List<SupervisorUserResponse>> queryFunc = userDependency::queryBatchSupervisor;
        BatchSupervisorRequest supervisorRequest = new BatchSupervisorRequest();
        supervisorRequest.setUidList(uidList);

        Consumer<List<String>> setUidListFunc = supervisorRequest::setUidList;


        return queryCachedInfo(uidList, supervisorRequest, setUidListFunc, keyFunc, queryFunc, deSerializerFunc, mapFunc);
    }

    /**
     * 查询用户信息
     *
     * @param saasId
     * @param uidList
     * @return
     */
    private Map<String, UserWideDTO> queryUserInfo(Long saasId, List<String> uidList) {
        Function<String, String> keyFunc = i -> String.format(ActivityRedisKeyConstant.USER_INFO_KEY, i);
        Function<String, UserWideDTO> deSerializerFunc = i -> JSON.parseObject(i, UserWideDTO.class);
        Function<UserWideDTO, String> mapFunc = UserWideDTO::getUid;
        Function<IrisUserWideListQueryRequest, List<UserWideDTO>> queryFunc = userDependency::batchQueryUserInfo;
        IrisUserWideListQueryRequest queryRequest = new IrisUserWideListQueryRequest();
        queryRequest.setSaasId(saasId);
        queryRequest.setUidList(uidList);

        Consumer<List<String>> setUidListFunc = queryRequest::setUidList;

        return queryCachedInfo(uidList, queryRequest, setUidListFunc, keyFunc, queryFunc, deSerializerFunc, mapFunc);
    }

    /**
     * 查询缓存信息
     *
     * @param uidList
     * @return
     */
    private <T, REQ> Map<String, T> queryCachedInfo(List<String> uidList, REQ req, Consumer<List<String>> setUidListFunc,
                                                    Function<String, String> keyFunc, Function<REQ, List<T>> queryFunc,
                                                    Function<String, T> deSerializerFunc, Function<T, String> mapFunc) {
        List<String> keys = uidList.stream().filter(i -> StrUtil.isNotBlank(i)).map(keyFunc)
                .collect(Collectors.toList());

        int size = keys.size();
        Map<String, Integer> notExistMap = Maps.newHashMapWithExpectedSize(size);
        List<String> values = redisClient.mget(keys.toArray(new String[size]));
        for (int i = 0; i < size; i++) {
            String val = values.get(i);
            if (StrUtil.isBlank(val)) {
                notExistMap.put(keys.get(i), i);
            }
        }

        if (CollectionUtil.isEmpty(notExistMap)) {
            return values.stream().map(deSerializerFunc).filter(i -> i != null)
                    .collect(Collectors.toMap(mapFunc, i -> i, (i1, i2) -> i1));
        }
        //缓存过期，重新查询
        List<String> expireUidList = uidList.stream()
                .filter(i -> notExistMap.keySet().contains(keyFunc.apply(i))).collect(Collectors.toList());
        setUidListFunc.accept(expireUidList);
        List<T> list = queryFunc.apply(req);
        if (CollectionUtil.isNotEmpty(list)) {
            for (T response : list) {
                String uid = mapFunc.apply(response);
                String cacheKey = keyFunc.apply(uid);
                //获取原索引，重新设置
                Integer idx = notExistMap.get(cacheKey);
                if (idx != null) {
                    values.set(idx, JSON.toJSONString(response));
                }
            }
            List<String> k = list.stream().filter(i -> i != null)
                    .map(mapFunc).map(keyFunc).collect(Collectors.toList());
            List<String> v = list.stream().filter(i -> i != null)
                    .map(i -> JSON.toJSONString(i)).collect(Collectors.toList());
            //缓存20到30分钟
            int expire = new Random().nextInt(1800 - 1200) + 1200;
            v.add(String.valueOf(expire));
            batchSetEx(k, v);
        }

        return values.stream().map(deSerializerFunc).filter(i -> i != null)
                .collect(Collectors.toMap(mapFunc, i -> i, (i1, i2) -> i1));
    }

    /**
     * 批量设置缓存和过期时间
     *
     * @param cacheKeys
     * @param cacheValues
     */
    private void batchSetEx(List<String> cacheKeys, List<String> cacheValues) {
        AssertUtils.isEqual(cacheKeys.size() + 1, cacheValues.size(), BizErrorCode.PARAM_CHECK_ERROR);
        redisClient.eval(BATCH_EXPIRE_SET_SCRIPT, cacheKeys, cacheValues);
    }


    /**
     * 处理单任务结果
     *
     * @param dataList
     * @param userInfoMap
     * @return
     */
    private List<ActivityTaskPageResponse> handleSingleTask(List<BatchDataResponse> dataList, Map<String, UserWideDTO> userInfoMap) {

        List<ActivityTaskPageResponse> results = Lists.newArrayList();
        for (BatchDataResponse response : dataList) {
            String uid = response.getUid();
            UserWideDTO userWideDTO = Optional.ofNullable(userInfoMap.get(uid))
                    .orElseGet(UserWideDTO::new);

            //导师信息
            String parentUid = response.getAdvisorUid();
            UserWideDTO tUserInfo = Optional.ofNullable(userInfoMap.get(parentUid))
                    .orElseGet(UserWideDTO::new);

            ActivityTaskPageResponse pageResponse = handleSingleTaskInfo(response);
            buildResponse(pageResponse, userInfoMap, userWideDTO, tUserInfo);
            results.add(pageResponse);
        }
        return results;
    }

    /**
     * 构建返回体
     *
     * @param pageResponse
     * @param userInfoMap
     * @param userWideDTO
     * @param tUserInfo
     */
    private void buildResponse(ActivityTaskPageResponse pageResponse, Map<String, UserWideDTO> userInfoMap, UserWideDTO userWideDTO, UserWideDTO tUserInfo) {

        UserWideDTO pUserInfo = Optional.ofNullable(userInfoMap.get(pageResponse.getPid()))
                .orElseGet(UserWideDTO::new);

        String unionId = Optional.ofNullable(userWideDTO.getChannelAccountList()).orElse(Collections.emptyList())
                .stream().filter(i -> Objects.equals(i.getChannelType(), "UNION_ID"))
                .findFirst().orElseGet(UserChannelAccountDTO::new).getChannelAccount();

        pageResponse.setHeadUrl(userWideDTO.getHeadUrl())
                .setNickname(userWideDTO.getNickName())
                .setUserLevel(userWideDTO.getLevelId())
                .setUnionId(unionId)
                .setPnickname(pUserInfo.getNickName())
                .setPheadUrl(pUserInfo.getHeadUrl())
                .setPuserLevel(pUserInfo.getLevelId())
                .setTuid(tUserInfo.getUid())
                .setTnickname(tUserInfo.getNickName())
                .setTheadUrl(tUserInfo.getHeadUrl())
                .setTuserLevel(tUserInfo.getLevelId());
    }

    /**
     * 分组处理多阶段任务
     *
     * @param dataList
     * @param userInfoMap
     * @return
     */
    private List<ActivityTaskPageResponse> handleMultiTask(List<BatchDataResponse> dataList, Map<String, UserWideDTO> userInfoMap) {
        Map<String, Map<String, List<BatchDataResponse>>> map = groupBy(dataList);

        List<ActivityTaskPageResponse> results = Lists.newArrayList();
        for (Map.Entry<String, Map<String, List<BatchDataResponse>>> entry : map.entrySet()) {
            String uid = entry.getKey();
            UserWideDTO userWideDTO = Optional.ofNullable(userInfoMap.get(uid))
                    .orElseGet(UserWideDTO::new);

            Map<String, List<BatchDataResponse>> map1 = entry.getValue();
            for (Map.Entry<String, List<BatchDataResponse>> listEntry : map1.entrySet()) {
                //导师信息
                String advisorUid = listEntry.getValue().get(0).getAdvisorUid();
                UserWideDTO tUserInfo = Optional.ofNullable(userInfoMap.get(advisorUid))
                        .orElseGet(UserWideDTO::new);

                ActivityTaskPageResponse pageResponse = mergeMultiTaskInfo(listEntry.getValue());
                buildResponse(pageResponse, userInfoMap, userWideDTO, tUserInfo);
                results.add(pageResponse);
            }
        }
        return results;
    }

    /**
     * 多阶段任务信息合并
     *
     * @param responseList
     * @return
     */
    private ActivityTaskPageResponse mergeMultiTaskInfo(List<BatchDataResponse> responseList) {
        ActivityTaskPageResponse response = getCurrentTask(responseList);

        //新版本状态和发奖信息
        List<BatchDataResponse> batchDataResponses = responseList.stream()
                .filter(i -> Objects.equals(String.valueOf(TaskRecordAwardIssueStatusEnum.ISSUE_SUCCEED.getCode()), i.getAwardIssueStatus()))
                .collect(Collectors.toList());
        List<SentAwardInfo> awardInfoList = batchDataResponses.stream()
                .map(activityTaskConvert::buildAwardInfoFromBatchDataResponse).collect(Collectors.toList());

        response.setSentAwardInfoList(awardInfoList)
                .setMultiTask(true);

        return response;
    }

    /**
     * 处理单任务状态任务信息
     *
     * @param batchDataResponse
     * @return
     */
    private ActivityTaskPageResponse handleSingleTaskInfo(BatchDataResponse batchDataResponse) {
        ActivityTaskPageResponse response = getCurrentTask(Arrays.asList(batchDataResponse));
        response.setMultiTask(false);
        return response;
    }

    /**
     * 获取当前任务信息
     *
     * @param responseList
     */
    private ActivityTaskPageResponse getCurrentTask(List<BatchDataResponse> responseList) {
        int totalStage = responseList.size();
        BatchDataResponse last = responseList.get(totalStage - 1);
        ActivityTaskPageResponse response = activityTaskConvert.buildActivityTaskPageResponse(last);
        response.setTotalStage(totalStage);
        //预估奖励金金额 = 用户活动中预估已完成的所有阶段的奖励金总和
        int estimateAwardAmount = responseList.stream().filter(i ->
                        Objects.equals(i.getEstimateUserTaskStatus(), TaskEstimateStatusEnum.FINISHED.getCode())
                                && !Objects.equals(i.getIsParticipated(), IsParticipatedEnum.LIMITED.name())
                                && Objects.nonNull(i.getSendAward()))
                .mapToInt(BatchDataResponse::getSendAward).sum();
        response.setEstimateAwardAmount(estimateAwardAmount);

        boolean auditing = responseList.stream().anyMatch(i -> Objects.equals(i.getUserTaskStatus(), UserTaskStatusEnum.PROGRESS.getCode())
                && Objects.equals(i.getEstimateUserTaskStatus(), TaskEstimateStatusEnum.FINISHED.getCode())
                && !Objects.equals(i.getIsParticipated(), IsParticipatedEnum.LIMITED.name()));
        response.setAuditing(auditing);

        //处理奖励佣金
        String extInfo = last.getExtInfo();
        if (StrUtil.isNotBlank(extInfo)) {
            try {
                JSONObject extInfoJson = Optional.ofNullable(JSON.parseObject(extInfo))
                        .orElseGet(JSONObject::new);
                JSONObject commission = Optional.ofNullable(extInfoJson.getJSONObject("commission"))
                        .orElseGet(JSONObject::new);
                Integer rewardIncomeAmt = Optional.ofNullable(commission.getInteger("rewardIncomeAmt")).orElse(0);
                Integer actualSalesRewardIncomeAmtAll = Optional.ofNullable(commission.getInteger("actualSalesRewardIncomeAmtAll"))
                        .orElse(0);
                response.setRewardIncomeAmt(rewardIncomeAmt)
                        .setActualSalesRewardIncomeAmtAll(actualSalesRewardIncomeAmtAll);
            } catch (Exception exception) {
                log.error("解析任务扩展字段失败，字段内容：{}", extInfo, exception);
            }
        }

        return handleCurrentTaskStatus(response, responseList);
    }

    /**
     * 处理任务状态
     *
     * @param response
     * @param responseList
     * @return
     */
    private ActivityTaskPageResponse handleCurrentTaskStatus(ActivityTaskPageResponse response, List<BatchDataResponse> responseList) {
        int totalStage = responseList.size();
        BatchDataResponse last = responseList.get(totalStage - 1);

        BatchDataResponse eFinishedStage = responseList.stream()
                .filter(i -> Objects.equals(TaskEstimateStatusEnum.FINISHED.getCode(), i.getEstimateUserTaskStatus()))
                .max(Comparator.comparing(BatchDataResponse::getTier)).orElse(null);
        Integer eStage = Optional.ofNullable(eFinishedStage).map(BatchDataResponse::getTier).orElse(0);

        BatchDataResponse finishedStage = responseList.stream()
                .filter(i -> Objects.equals(UserTaskStatusEnum.FINISHED.getCode(), i.getUserTaskStatus()))
                .max(Comparator.comparing(BatchDataResponse::getTier)).orElse(null);
        Integer fStage = Optional.ofNullable(finishedStage).map(BatchDataResponse::getTier).orElse(0);

        int maxStage = responseList.stream().max(Comparator.comparing(BatchDataResponse::getTier))
                .map(BatchDataResponse::getTier).orElse(0);
        Integer minStage = responseList.stream().min(Comparator.comparing(BatchDataResponse::getTier))
                .map(BatchDataResponse::getTier).orElse(0);
        boolean extend = maxStage != totalStage;
        //部分继承的数据
        if (extend) {
            fStage = Math.max(fStage, minStage - 1);
            eStage = Math.max(eStage, minStage - 1);
        }

        //e<f：不存在（最大预估完成阶数不应小于最大实际完成阶数）
        TaskEndStatusEnum taskEndTimeStatus = TaskEndStatusEnum.getCodeByTaskEndTime(DateUtil.date(last.getCurrentTaskEndTime()), last.getCommissionTaskPlusDay());
        switch (taskEndTimeStatus) {
            case PROGRESS:
                //e=f=最高：最高阶预估已完成且实际已完成，展示“已完成”
                if (eStage == fStage && eStage == maxStage) {
                    return response.setStage(maxStage)
                            .setCommonTaskStatus(MultiTaskStatusEnum.FINISHED.getCode());
                }

                Integer finalFStage = fStage;
                Integer progressingStage = responseList.stream().filter(i -> i.getTier() != null && i.getTier() > finalFStage)
                        .findFirst().map(BatchDataResponse::getTier).orElse(fStage + 1);
                //e≥f：（审核中：e阶，不展示）展示“进行中 （大于f的下一阶）”
                return response.setStage(progressingStage)
                        .setCommonTaskStatus(MultiTaskStatusEnum.PROGRESS.getCode());
            case PRE_END:
                //e=f=0：展示“未完成”
                if (eStage == fStage && eStage == 0) {
                    return response.setStage(0)
                            .setCommonTaskStatus(MultiTaskStatusEnum.UNFINISHED.getCode());
                }
                //e=f≠0：展示“已完成f阶”
                if (eStage == fStage && eStage != 0) {
                    //继承终态需判断阶数是上级还是下级
                    if (fStage < minStage) {
                        //已完成阶数为老上级，新上级无已完成
                        return response.setStage(0)
                                .setCommonTaskStatus(MultiTaskStatusEnum.UNFINISHED.getCode());
                    }

                    return response.setStage(fStage)
                            .setCommonTaskStatus(MultiTaskStatusEnum.FINISHED.getCode());
                }
                //e>f：展示“审核中e阶”
                return response.setStage(eStage)
                        .setCommonTaskStatus(MultiTaskStatusEnum.AUDITING.getCode());
            case END:
                //f=0：展示“未完成”
                if (fStage == 0) {
                    return response.setStage(0)
                            .setCommonTaskStatus(MultiTaskStatusEnum.UNFINISHED.getCode());
                }

                //继承终态需判断阶数是上级还是下级
                if (fStage < minStage) {
                    //已完成阶数为老上级，新上级无已完成
                    return response.setStage(0)
                            .setCommonTaskStatus(MultiTaskStatusEnum.UNFINISHED.getCode());
                }

                //e≥f：展示“已完成f阶”
                return response.setStage(fStage)
                        .setCommonTaskStatus(MultiTaskStatusEnum.FINISHED.getCode());
            default:
                break;
        }

        return response;
    }

    /**
     * 分组
     *
     * @param responses
     * @return
     */
    private Map<String, Map<String, List<BatchDataResponse>>> groupBy(List<BatchDataResponse> responses) {
        return responses.stream()
                //下级uid分组
                .collect(Collectors.groupingBy(BatchDataResponse::getUid,
                        //根据活动id，任务id，任务周期 分组
                        Collectors.groupingBy(i -> {
                                    if (StrUtil.isNotBlank(i.getGroupByExpression())) {
                                        return SpelUtils.parse(i.getGroupByExpression(), i, String.class);
                                    }
                                    return i.getId();
                                },
                                Collectors.collectingAndThen(Collectors.toList(),
                                        l -> l.stream().sorted(Comparator.comparing(BatchDataResponse::getTier))
                                                .collect(Collectors.toList())
                                )
                        )));
    }
}
